//
//  main.swift
//  Decorator
//
//  Created by LY on 2017/10/21.
//  Copyright © 2017年 DesignPatterns. All rights reserved.
//

import Foundation

class CustomAccount {
    let customerName:String
    var purchaes = [Purchase]()
    init(name:String) {
        customerName = name
    }
    func addPurchase(purchase:Purchase){
        self.purchaes.append(purchase)
    }
    func cleanPurchase(){
        purchaes.removeAll()
        print("已清空")
    }
    func printAccount(){
        var total:Float = 0.0
        for item in purchaes{
            total = total + item.totalPrice
            print("Purchase :\(item.description) price\(formartCurrencyString(number: item.totalPrice))")
        }
        print("total due:\(total)")
    }
    func formartCurrencyString(number:Float) -> String{
        let foramatter = NumberFormatter()
        foramatter.numberStyle = .currency
        return foramatter.string(from: NSNumber(value: number)) ?? ""
    }
}
//模拟购物一
let account = CustomAccount(name: "Joe")
account.addPurchase(purchase: Purchase(product: "Red Hat", price: 10))
account.addPurchase(purchase: Purchase(product: "Scarf", price: 20))
account.printAccount()
//当需要更多类型的商品是，仅需增加Purchase子类即可
class PurchaseWithGiftWrap:Purchase{
    override var description: String{
        return "\(super.description) + PurchaseWithGiftWrap"
    }
    override var totalPrice: Float{
        return super.totalPrice + 2
    }
}
class PurchaseWithRibbon:Purchase{
    override var description: String{
        return "\(super.description) + PurchaseWithRibbon"
    }
    override var totalPrice: Float{
        return super.totalPrice + 1
    }
}
//模拟购物二
account.addPurchase(purchase: PurchaseWithRibbon(product: "ribbon", price: 100))
account.addPurchase(purchase: PurchaseWithGiftWrap(product: "wrap", price: 45))
account.printAccount()
/**
 虽则服务类型的多样化可能需要创建更多的子类，而过多了子类会令代码混乱难以为继
 装饰器用于解决上述问题而产生，装饰器类是指用于封装原始类并改变其行为的类。装饰器类对外界提供的API与其封装的原类相同，而且为了创建其他组合，装饰器还可以封装到其他装饰器。
 由于装饰器向外界提供的方法和属性与原来的类一模一样，因此可以在不修改调用组件的情况下用他们替换原来的类。装饰器通常会调用他们封装的类的方法和属性。鉴于所有对象暴露的方法和属性都一样，因此装饰器也不知晓它封装的是原始类实例还是另一个装饰器
 */
class BasePurchaseDecorator:Purchase{
    private let wappedPurchase:Purchase
    init(purchase:Purchase) {
        wappedPurchase = purchase
        super.init(product: purchase.description, price: purchase.totalPrice)
    }
}
class PurchaseWithGiftWrapB:BasePurchaseDecorator {
    override var description: String{
        return "\(super.description) + GiftWrapB"
    }
    override var totalPrice: Float{
        return super.totalPrice + 2
    }
}
class PurchaseWithRibbonB : BasePurchaseDecorator{
    override var description: String{
        return "\(super.description) + RibbonB"
    }
    override var totalPrice: Float{
        return super.totalPrice + 5
    }
}
class PurchaseWithDelivery : BasePurchaseDecorator{
    override var description: String{
        return "\(super.description) + Delivery"
    }
    override var totalPrice: Float{
        return super.totalPrice + 10
    }
}
account.cleanPurchase()
account.addPurchase(purchase: Purchase(product: "Red hat", price: 10))
account.addPurchase(purchase: Purchase(product: "Scarf", price: 20))
account.addPurchase(purchase: PurchaseWithDelivery(purchase: PurchaseWithGiftWrapB(purchase: Purchase(product: "Sunglass", price: 25))))
account.printAccount()
//MARK 变体一
/**
 变体一：
 创建具有新功能的装饰器
 与之前介绍的装饰器不相同的地方是，使用装饰器为原有的对象增加新的方法或者属性。
 好处增强了装饰器的灵活性，但同时减小了其适用范围
 */
class DiscountDecorator:Purchase{
    private let wrappedPurchase : Purchase
    init(purchase : Purchase) {
        wrappedPurchase = purchase
        super.init(product: purchase.description, price: purchase.totalPrice)
    }
    var discountAmount : Float{
        return 0
    }
    func countDiscounts() -> Int {
        var total = 1
        if let discounter = wrappedPurchase as? DiscountDecorator{
            total = total + discounter.countDiscounts()
        }
        return total
    }
}
class BlackFridayDecorator:DiscountDecorator{
    override var totalPrice: Float{
        return super.totalPrice - discountAmount
    }
    override var discountAmount: Float{
        return super.totalPrice * 0.2
    }
}
class EndLineDecorator: DiscountDecorator {
    override var totalPrice: Float{
        return super.totalPrice - discountAmount
    }
    override var discountAmount: Float{
        return super.totalPrice * 0.7
    }
}
account.cleanPurchase()
account.addPurchase(purchase: Purchase(product: "Red hat", price: 10))
account.addPurchase(purchase: Purchase(product: "Scarf", price: 20))
account.addPurchase(purchase: EndLineDecorator(purchase: BlackFridayDecorator(purchase: PurchaseWithDelivery(purchase: PurchaseWithGiftWrapB(purchase: Purchase(product: "Sunglass", price: 25))))))
account.printAccount()
for item in account.purchaes{
    if let  itemA  = item as? DiscountDecorator{
        print("\(itemA) has \(itemA.countDiscounts()) Discounts")
    }else{
        print("\(item) has no Discounts")
    }
}
/**
 弊端：
 包含新功能的装饰器，必须是信装饰器或其子类
 否则可能服务达到预期效果
 */
account.cleanPurchase()
account.addPurchase(purchase: Purchase(product: "Red hat", price: 10))
account.addPurchase(purchase: Purchase(product: "Scarf", price: 20))
account.addPurchase(purchase: EndLineDecorator(purchase: PurchaseWithDelivery(purchase: BlackFridayDecorator(purchase: PurchaseWithGiftWrapB(purchase: Purchase(product: "Sunglass", price: 25))))))
account.printAccount()
for item in account.purchaes{
    if let  itemA  = item as? DiscountDecorator{
        print("\(itemA) has \(itemA.countDiscounts()) Discounts")
    }else{
        print("\(item) has no Discounts")
    }
}
/**
 合并装饰器
 目的：
 对对象做多次修改
 注意：
 装饰器虽然可以随意创建自己的方法实现和定义属性，但是这些方法和属性所执行的任务应用原类保持一致。装饰器的作用应该是增强或者扩展原始类的功能，而不是给现有的API渗透功能。
 */
class GiftOptinDecorator:Purchase{
    private let wappedPurchase:Purchase
    private let options:[OPTION]
    enum OPTION {
        case GIFWARP
        case RIBBON
        case DELIVERY
    }
    init(purchase:Purchase,options:OPTION...) {
        wappedPurchase = purchase
        self.options = options
        super.init(product: purchase.description, price: purchase.totalPrice)
    }
    override var description: String{
        var result = wappedPurchase.description
        for option in options{
            switch (option){
            case .GIFWARP:
//                result ="\(result) + gifwrap"
                result.append("gifwra")
            case .RIBBON:
//                result = "\(result) + ribbon"
                result.append("ribbon")
            case .DELIVERY:
//                result = "re"
                result.append("delivery")
            }
        }
        return result
    }
    override var totalPrice: Float{
        var result = wappedPurchase.totalPrice
        for option in options {
            switch option {
            case .GIFWARP:
                result =  result + 2
            case .RIBBON:
                result =  result + 1
            case .DELIVERY:
                result =  result + 5
            }
        }
        return result
    }
}

account.cleanPurchase()

account.addPurchase(purchase: Purchase(product: "Red Hat", price: 10))
account.addPurchase(purchase: Purchase(product: "Scarf", price: 20))
account.addPurchase(purchase: EndLineDecorator(purchase: BlackFridayDecorator(purchase: GiftOptinDecorator(purchase: Purchase(product: "Sunglass", price: 25), options: .GIFWARP,.DELIVERY))))
account.printAccount()
for item in account.purchaes{
    if let  itemA  = item as? DiscountDecorator{
        print("\(itemA) has \(itemA.countDiscounts()) Discounts")
    }else{
        print("\(item) has no Discounts")
    }
}
